/****************************************************************************
 * Copyright 2010 Huascar A. Sanchez                                        *
 *                                                                          *
 * Licensed under the Apache License, Version 2.0 (the "License");          *
 * you may not use this file except in compliance with the License.         *
 * You may obtain a copy of the License at                                  *
 *                                                                          *
 *     http://www.apache.org/licenses/LICENSE-2.0                           *
 *                                                                          *
 * Unless required by applicable law or agreed to in writing, software      *
 * distributed under the License is distributed on an "AS IS" BASIS,        *
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
 * See the License for the specific language governing permissions and      *
 * limitations under the License.                                           *
 ****************************************************************************/
package com.gotobject.greatq.datastore;

import com.google.appengine.api.datastore.Transaction;
import org.jiql.JIQLGDataUtil;
import org.jiql.jdbc.ResultSet;
import org.jiql.jdbc.jiqlConnection;

import java.sql.Array;
import java.sql.Blob;
import java.sql.CallableStatement;
import java.sql.Clob;
import java.sql.DatabaseMetaData;
import java.sql.NClob;
import java.sql.PreparedStatement;
import java.sql.SQLClientInfoException;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.sql.SQLWarning;
import java.sql.SQLXML;
import java.sql.Savepoint;
import java.sql.Statement;
import java.sql.Struct;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @author huascar.sanchez@gmail.com (Huascar A. Sanchez)
 */
public class DatastoreConnection extends jiqlConnection {
    // todo(huascar) complete this class, and five more: Statement, Driver, SQLParser, 
    private final AtomicReference<Properties>       params          = new AtomicReference<Properties>();
    private final AtomicInteger                     isolation       = new AtomicInteger();
    private final AtomicInteger                     foundrows       = new AtomicInteger();
    private final AtomicInteger                     identity        = new AtomicInteger();
    private final AtomicBoolean                     isClosed        = new AtomicBoolean();
    private final AtomicBoolean                     autoCommit      = new AtomicBoolean();
    private final AtomicReference<Transaction>      active          = new AtomicReference<Transaction>();
    private final DateEncoding                      dateEncoding    = new DateEncoding();
    private final CatalogRef                        catalog         = new CatalogRef();


    public DatastoreConnection() {
        this(new Properties());
    }

    public DatastoreConnection(Properties properties) {
        super(properties);
        params.compareAndSet(params.get(), properties.isEmpty() ? null : properties);
        JIQLGDataUtil.removeThread();
    }

    @Override
    public void setFoundRows(int number) {
        foundrows.compareAndSet(getFoundRows(), number);
    }

    @Override
    public int getFoundRows() {
        return foundrows.get();
    }

    @Override
    public void setIdentity(int number) {
        if(number <= 0) return;
        identity.compareAndSet(getIdentity(), number);
    }

    @Override
    public int getIdentity() {
        return identity.get();
    }

    String propertyOf(String key){
        if(getProperties() == null) return null;
        return getProperties().getProperty(key);
    }

    @Override
    public DateFormat getDateFormat() {
        if(dateEncoding.dateFormat != null) return dateEncoding.dateFormat;
        dateEncoding.dateFormat = new SimpleDateFormat(getDatePattern());
        return dateEncoding.dateFormat;
    }

    @Override
    public String getDatePattern() {
        if(dateEncoding.datePattern != null) return dateEncoding.datePattern;
        dateEncoding.datePattern = propertyOf("date.format");
        return dateEncoding.datePattern == null ? "yyyy-MM-dd HH:mm:ss" : dateEncoding.datePattern;
    }

    @Override
    public boolean isRemote() {
        final String url = propertyOf("url");
        return url != null && url.startsWith("http");
    }

    @Override
    public Properties getProperties() {
        return params.get();
    }

    @Override
    public boolean isWrapperFor(Class<?> aClass) throws SQLException {
        return false;
    }

    @Override
    public <T> T unwrap(Class<T> tClass) throws SQLException {
        return null;
    }

    @Override
    public void clearWarnings() throws SQLException {
        // does nothing
    }

    @Override
    public void close() throws SQLException {
        isClosed.set(true);
    }

    @Override
    public void close(boolean b) throws SQLException {
        isClosed.compareAndSet(isClosed.get(), b);
    }

    @Override
    public void commit() throws SQLException {
        if(active.get() == null) return;
        active.get().commit();
        active.set(null);
    }


    @Override
    public Array createArrayOf(String s, Object[] objects) throws SQLException {
        throw new SQLFeatureNotSupportedException("error: method not supported");
    }

    @Override
    public Blob createBlob() throws SQLException {
        throw new SQLFeatureNotSupportedException("error: method not supported");
    }

    @Override
    public Clob createClob() throws SQLException {
        throw new SQLFeatureNotSupportedException("error: method not supported");
    }

    @Override
    public NClob createNClob() throws SQLException {
        throw new SQLFeatureNotSupportedException("error: method not supported");
    }

    @Override
    public SQLXML createSQLXML() throws SQLException {
        throw new SQLFeatureNotSupportedException("error: method not supported");
    }

    @Override
    public Statement createStatement() throws SQLException {
        return new DatastoreStatement(new org.jiql.jdbc.Statement(this));
    }

    @Override
    public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
        return createStatement();
    }

    @Override
    public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
        throw new SQLFeatureNotSupportedException("error: method not supported");
    }

    @Override
    public Struct createStruct(String s, Object[] objects) throws SQLException {
        throw new SQLFeatureNotSupportedException("error: method not supported");
    }

    @Override
    public boolean getAutoCommit() throws SQLException {
        return autoCommit.get();
    }

    @Override
    public String getCatalog() throws SQLException {
        return catalog.catalog == null ? propertyOf("Catalog") : catalog.catalog;
    }

    @Override
    public Properties getClientInfo() throws SQLException {
        throw new SQLFeatureNotSupportedException("error: method not supported");
    }

    @Override
    public String getClientInfo(String s) throws SQLException {
        throw new SQLFeatureNotSupportedException("error: method not supported");
    }

    @Override
    public int getHoldability() throws SQLException {
        return ResultSet.CLOSE_CURSORS_AT_COMMIT;
    }

    @Override
    public DatabaseMetaData getMetaData() throws SQLException {
        // todo(Huascar) implement my own metadata
        return super.getMetaData();
    }

    @Override
    public int getTransactionIsolation() throws SQLException {
        return isolation.get();
    }

    @Override
    public Map<String, Class<?>> getTypeMap() throws SQLException {
         throw new SQLFeatureNotSupportedException("error: method not supported");
    }

    @Override
    public SQLWarning getWarnings() throws SQLException {
        return null;
    }

    @Override
    public boolean isClosed() throws SQLException {
        return isClosed.get();
    }

    @Override
    public void setClosed(boolean b) {
        isClosed.compareAndSet(isClosed.get(), b);
    }

    @Override
    public boolean isReadOnly() throws SQLException {
        return false;
    }

    @Override
    public boolean isValid(int i) throws SQLException {
        return (!isClosed());
    }

    @Override
    public String nativeSQL(String s) throws SQLException {
        throw new SQLFeatureNotSupportedException("error: method not supported");
    }

    @Override
    public CallableStatement prepareCall(String sql) throws SQLException {
        throw new SQLFeatureNotSupportedException("error: method not supported");
    }

    @Override
    public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
        throw new SQLFeatureNotSupportedException("error: method not supported");
    }

    @Override
    public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
        throw new SQLFeatureNotSupportedException("error: method not supported");
    }

    @Override
    public PreparedStatement prepareStatement(String sql) throws SQLException {
        return super.prepareStatement(sql);
    }

    @Override
    public PreparedStatement prepareStatement(String s, int i) throws SQLException {
        throw new SQLFeatureNotSupportedException("error: method not supported");
    }

    @Override
    public PreparedStatement prepareStatement(String s, int[] ints) throws SQLException {
        throw new SQLFeatureNotSupportedException("error: method not supported");
    }

    @Override
    public PreparedStatement prepareStatement(String s, int i, int i1) throws SQLException {
        throw new SQLFeatureNotSupportedException("error: method not supported");
    }

    @Override
    public PreparedStatement prepareStatement(String s, int i, int i1, int i2) throws SQLException {
        throw new SQLFeatureNotSupportedException("error: method not supported");
    }

    @Override
    public PreparedStatement prepareStatement(String s, String[] strings) throws SQLException {
        throw new SQLFeatureNotSupportedException("error: method not supported");
    }

    @Override
    public void releaseSavepoint(Savepoint savepoint) throws SQLException {
       throw new SQLFeatureNotSupportedException("error: method not supported");
    }

    @Override
    public void rollback() throws SQLException {
        if(active.get() != null && !active.get().isActive()){
            active.get().rollback();
            setTransaction(null);
        }
    }

    @Override
    public void rollback(Savepoint savepoint) throws SQLException {
        throw new SQLFeatureNotSupportedException("error: method not supported");
    }

    @Override
    public void setTransaction(Transaction transaction) throws SQLException {
        active.compareAndSet(active.get(), transaction);
    }

    @Override
    public void setAutoCommit(boolean b) throws SQLException {
        if(!autoCommit.get() && !isRemote() && (isTransactionEnabled())){
            autoCommit.set(false);
        } else {
            autoCommit.set(true);
        }
    }

    boolean isTransactionEnabled(){
        return (propertyOf("enable.transactions") != null && propertyOf("enable.transactions").equals("true"));
    }

    @Override
    public void setCatalog(String s) throws SQLException {
        catalog.catalog = s;
    }

    @Override
    public void setClientInfo(Properties properties) throws SQLClientInfoException {
        // does nothing
    }

    @Override
    public void setClientInfo(String name, String value) throws SQLClientInfoException {
        //does nothing
    }

    @Override
    public void setHoldability(int i) throws SQLException {
        // does nothing
    }

    @Override
    public void setReadOnly(boolean b) throws SQLException {
        //does nothing
    }

    @Override
    public Savepoint setSavepoint() throws SQLException {
        throw new SQLFeatureNotSupportedException("error: method not supported");
    }

    @Override
    public Savepoint setSavepoint(String name) throws SQLException {
        throw new SQLFeatureNotSupportedException("error: method not supported");
    }

    @Override
    public void setTransactionIsolation(int level) throws SQLException {
        isolation.compareAndSet(isolation.get(), level);
    }

    @Override
    public void setTypeMap(Map<String, Class<?>> stringClassMap) throws SQLException {
        throw new SQLFeatureNotSupportedException("error: method not supported");
    }


    static class DateEncoding {
        DateFormat dateFormat;
        String     datePattern;
    }

    static class CatalogRef {
        String catalog = null;
    }


}
